View Javadoc
1 /* 2 * The Apache Software License, Version 1.1 3 * 4 * Copyright (c) 2002 The Apache Software Foundation. All rights 5 * reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * 3. The end-user documentation included with the redistribution, 20 * if any, must include the following acknowledgment: 21 * "This product includes software developed by the 22 * Apache Software Foundation (http://www.apache.org/)." 23 * Alternately, this acknowledgment may appear in the software itself, 24 * if and wherever such third-party acknowledgments normally appear. 25 * 26 * 4. The names "Apache" and "Apache Software Foundation" must 27 * not be used to endorse or promote products derived from this 28 * software without prior written permission. For written 29 * permission, please contact apache@apache.org. 30 * 31 * 5. Products derived from this software may not be called "Apache", 32 * nor may "Apache" appear in their name, without prior written 33 * permission of the Apache Software Foundation. 34 * 35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 * SUCH DAMAGE. 47 * ==================================================================== 48 * 49 * This software consists of voluntary contributions made by many 50 * individuals on behalf of the Apache Software Foundation. For more 51 * information on the Apache Software Foundation, please see 52 * <http://www.apache.org/>;. 53 */ 54 package net.sf.cglib.proxy; 55 56 import net.sf.cglib.core.*; 57 import java.lang.reflect.Method; 58 import java.util.*; 59 import org.objectweb.asm.ClassVisitor; 60 61 /*** 62 * Generates dynamic subclasses to enable method interception. This 63 * class started as a substitute for the standard Dynamic Proxy support 64 * included with JDK 1.3, but one that allowed the proxies to extend a 65 * concrete base class, in addition to implementing interfaces. The dynamically 66 * generated subclasses override the non-final methods of the superclass and 67 * have hooks which callback to user-defined interceptor 68 * implementations. 69 * <p> 70 * The original and most general callback type is the {@link MethodInterceptor}, which 71 * in AOP terms enables "around advice"--that is, you can invoke custom code both before 72 * and after the invocation of the "super" method. In addition you can modify the 73 * arguments before calling the super method, or not call it at all. 74 * <p> 75 * Although <code>MethodInterceptor</code> is generic enough to meet any 76 * interception need, it is often overkill. For simplicity and performance, additional 77 * specialized callback types, such as {@link LazyLoader} are also available. 78 * Often a single callback will be used per enhanced class, but you can control 79 * which callback is used on a per-method basis with a {@link CallbackFilter}. 80 * <p> 81 * The most common uses of this class are embodied in the static helper methods. For 82 * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create 83 * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern. 84 * <p> 85 * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is 86 * used to explicitly disable this feature. The <code>Factory</code> interface provides an API 87 * to change the callbacks of an existing object, as well as a faster and easier way to create 88 * new instances of the same type. 89 * <p> 90 * For an almost drop-in replacement for 91 * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class. 92 */ 93 public class Enhancer extends AbstractClassGenerator 94 { 95 private static final Source SOURCE = new Source(Enhancer.class.getName()); 96 private static final EnhancerKey KEY_FACTORY = 97 (EnhancerKey)KeyFactory.create(EnhancerKey.class, KeyFactory.CLASS_BY_NAME); 98 99 interface EnhancerKey { 100 public Object newInstance(Class type, 101 Class[] interfaces, 102 CallbackFilter filter, 103 Class[] callbackTypes, 104 boolean useFactory); 105 } 106 107 private Class[] interfaces; 108 private CallbackFilter filter; 109 private Callback[] callbacks; 110 private Class[] callbackTypes; 111 private boolean classOnly; 112 private Class superclass; 113 private Class[] argumentTypes; 114 private Object[] arguments; 115 private boolean useFactory = true; 116 117 /*** 118 * Create a new <code>Enhancer</code>. A new <code>Enhancer</code> 119 * object should be used for each generated object, and should not 120 * be shared across threads. To create additional instances of a 121 * generated class, use the <code>Factory</code> interface. 122 * @see Factory 123 */ 124 public Enhancer() { 125 super(SOURCE); 126 } 127 128 /*** 129 * Set the class which the generated class will extend. As a convenience, 130 * if the supplied superclass is actually an interface, <code>setInterfaces</code> 131 * will be called with the appropriate argument instead. 132 * A non-interface argument must not be declared as final, and must have an 133 * accessible constructor. 134 * @param superclass class to extend or interface to implement 135 * @see #setInterfaces(Class[]) 136 */ 137 public void setSuperclass(Class superclass) { 138 if (superclass != null && superclass.isInterface()) { 139 setInterfaces(new Class[]{ superclass }); 140 } else if (superclass != null && superclass.equals(Object.class)) { 141 // affects choice of ClassLoader 142 this.superclass = null; 143 } else { 144 this.superclass = superclass; 145 } 146 } 147 148 /*** 149 * Set the interfaces to implement. The <code>Factory</code> interface will 150 * always be implemented regardless of what is specified here. 151 * @param interfaces array of interfaces to implement, or null 152 * @see Factory 153 */ 154 public void setInterfaces(Class[] interfaces) { 155 this.interfaces = interfaces; 156 } 157 158 /*** 159 * Set the {@link CallbackFilter} used to map the generated class' methods 160 * to a particular callback index. 161 * New object instances will always use the same mapping, but may use different 162 * actual callback objects. 163 * @param filter the callback filter to use when generating a new class 164 * @see #setCallbacks 165 */ 166 public void setCallbackFilter(CallbackFilter filter) { 167 this.filter = filter; 168 } 169 170 /*** 171 * Set the single {@link Callback} to use. 172 * Ignored if you use {@link #createClass}. 173 * @param callback the callback to use for all methods 174 * @see #setCallbacks 175 */ 176 public void setCallback(final Callback callback) { 177 setCallbacks(new Callback[]{ callback }); 178 } 179 180 /*** 181 * Set the array of callbacks to use. 182 * Ignored if you use {@link #createClass}. 183 * You must use a {@link CallbackFilter} to specify the index into this 184 * array for each method in the proxied class. 185 * @param callbacks the callback array 186 * @see #setCallbackFilter 187 * @see #setCallback 188 */ 189 public void setCallbacks(Callback[] callbacks) { 190 if (callbacks != null && callbacks.length == 0) { 191 throw new IllegalArgumentException("Array cannot be empty"); 192 } 193 this.callbacks = callbacks; 194 } 195 196 /*** 197 * Set whether the enhanced object instances should implement 198 * the {@link Factory} interface. 199 * This was added for tools that need for proxies to be more 200 * indistinguishable from their targets. Also, in some cases it may 201 * be necessary to disable the <code>Factory</code> interface to 202 * prevent code from changing the underlying callbacks. 203 * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code> 204 */ 205 public void setUseFactory(boolean useFactory) { 206 this.useFactory = useFactory; 207 } 208 209 /*** 210 * Set the single type of {@link Callback} to use. 211 * This may be used instead of {@link #setCallback} when calling 212 * {@link #createClass}, since it may not be possible to have 213 * an array of actual callback instances. 214 * @param callbackType the type of callback to use for all methods 215 * @see #setCallbackTypes 216 */ 217 public void setCallbackType(Class callbackType) { 218 setCallbackTypes(new Class[]{ callbackType }); 219 } 220 221 /*** 222 * Set the array of callback types to use. 223 * This may be used instead of {@link #setCallbacks} when calling 224 * {@link #createClass}, since it may not be possible to have 225 * an array of actual callback instances. 226 * You must use a {@link CallbackFilter} to specify the index into this 227 * array for each method in the proxied class. 228 * @param callbackTypes the array of callback types 229 */ 230 public void setCallbackTypes(Class[] callbackTypes) { 231 if (callbackTypes != null && callbackTypes.length == 0) { 232 throw new IllegalArgumentException("Array cannot be empty"); 233 } 234 this.callbackTypes = callbackTypes; 235 } 236 237 /*** 238 * Generate a new class if necessary and uses the specified 239 * callbacks (if any) to create a new object instance. 240 * Uses the no-arg constructor of the superclass. 241 * @return a new instance 242 */ 243 public Object create() { 244 classOnly = false; 245 argumentTypes = null; 246 return createHelper(); 247 } 248 249 /*** 250 * Generate a new class if necessary and uses the specified 251 * callbacks (if any) to create a new object instance. 252 * Uses the constructor of the superclass matching the <code>argumentTypes</code> 253 * parameter, with the given arguments. 254 * @param argumentTypes constructor signature 255 * @param arguments compatible wrapped arguments to pass to constructor 256 * @return a new instance 257 */ 258 public Object create(Class[] argumentTypes, Object[] arguments) { 259 classOnly = false; 260 if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) { 261 throw new IllegalArgumentException("Arguments must be non-null and of equal length"); 262 } 263 this.argumentTypes = argumentTypes; 264 this.arguments = arguments; 265 return createHelper(); 266 } 267 268 /*** 269 * Generate a new class if necessary and return it without creating a new instance. 270 * This ignores any callbacks that have been set. 271 * To create a new instance you will have to use reflection, and methods 272 * called during the constructor will not be intercepted. To avoid this problem, 273 * use the multi-arg <code>create</code> method. 274 * @see #create(Class[], Object[]) 275 */ 276 public Class createClass() { 277 classOnly = true; 278 return (Class)createHelper(); 279 } 280 281 private Object createHelper() { 282 if (classOnly ^ (callbacks == null)) { 283 if (classOnly) { 284 throw new IllegalStateException("createClass does not accept callbacks"); 285 } else { 286 throw new IllegalStateException("callbacks are required unless using createClass"); 287 } 288 } 289 if (callbacks == null && callbackTypes == null) { 290 throw new IllegalStateException("Either callbacks or callback types are always required"); 291 } 292 if (callbacks != null && callbackTypes != null) { 293 if (callbacks.length != callbackTypes.length) { 294 throw new IllegalStateException("Lengths of callback and callback types array must be the same"); 295 } 296 for (int i = 0; i < callbacks.length; i++) { 297 // make sure all classes are callbacks 298 CallbackUtils.getGenerator(callbackTypes[i]); 299 if (callbacks[i] == null) { 300 throw new IllegalStateException("Callback cannot be null"); 301 } 302 if (!callbackTypes[i].isAssignableFrom(callbacks[i].getClass())) { 303 throw new IllegalStateException("Callback " + callbacks[i] + " is not assignable to " + callbackTypes[i]); 304 } 305 } 306 } else if (callbacks != null) { 307 callbackTypes = new Class[callbacks.length]; 308 for (int i = 0; i < callbacks.length; i++) { 309 callbackTypes[i] = CallbackUtils.determineType(callbacks[i]); 310 } 311 } else { 312 for (int i = 0; i < callbackTypes.length; i++) { 313 // make sure all classes are callbacks 314 CallbackUtils.getGenerator(callbackTypes[i]); 315 } 316 } 317 if (filter == null) { 318 if (callbackTypes.length > 1) { 319 throw new IllegalStateException("Multiple callback types possible but no filter specified"); 320 } 321 filter = CallbackFilter.ALL_ZERO; 322 } 323 324 if (superclass != null) { 325 setNamePrefix(superclass.getName()); 326 } else if (interfaces != null) { 327 setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName()); 328 } 329 Object key = KEY_FACTORY.newInstance(superclass, interfaces, filter, callbackTypes, useFactory); 330 return super.create(key); 331 } 332 333 protected ClassLoader getDefaultClassLoader() { 334 if (superclass != null) { 335 return superclass.getClassLoader(); 336 } else if (interfaces != null) { 337 return interfaces[0].getClassLoader(); 338 } else { 339 return null; 340 } 341 } 342 343 public void generateClass(ClassVisitor v) throws Exception { 344 new EnhancerEmitter(v, getClassName(), superclass, interfaces, filter, callbackTypes, useFactory); 345 } 346 347 protected Object firstInstance(Class type) throws Exception { 348 if (classOnly) { 349 return type; 350 } else { 351 return createUsingReflection(type); 352 } 353 } 354 355 protected Object nextInstance(Object instance) { 356 Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass(); 357 if (classOnly) { 358 return protoclass; 359 } else if (instance instanceof Factory) { 360 if (argumentTypes != null) { 361 return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks); 362 } else { 363 return ((Factory)instance).newInstance(callbacks); 364 } 365 } else { 366 return createUsingReflection(protoclass); 367 } 368 } 369 370 /*** 371 * Call this method to register the {@link Callback} array to use before 372 * creating a new instance of the generated class via reflection. If you are using 373 * an instance of <code>Enhancer</code> or the {@link Factory} interface to create 374 * new instances, this method is unnecessary. Its primary use is for when you want to 375 * cache and reuse a generated class yourself, and the generated class does 376 * <i>not</i> implement the {@link Factory} interface. 377 * @see #setUseFactory 378 */ 379 public static void registerCallbacks(Class generatedClass, Callback[] callbacks) { 380 EnhancerEmitter.setThreadCallbacks(generatedClass, callbacks); 381 } 382 383 private Object createUsingReflection(Class type) { 384 EnhancerEmitter.setThreadCallbacks(type, callbacks); 385 if (argumentTypes != null) { 386 return ReflectUtils.newInstance(type, argumentTypes, arguments); 387 } else { 388 return ReflectUtils.newInstance(type); 389 } 390 } 391 392 /*** 393 * Helper method to create an intercepted object. 394 * For finer control over the generated instance, use a new instance of <code>Enhancer</code> 395 * instead of this static method. 396 * @param type class to extend or interface to implement 397 * @param callback the callback to use for all methods 398 */ 399 public static Object create(Class type, Callback callback) { 400 Enhancer e = new Enhancer(); 401 e.setSuperclass(type); 402 e.setCallback(callback); 403 return e.create(); 404 } 405 406 /*** 407 * Helper method to create an intercepted object. 408 * For finer control over the generated instance, use a new instance of <code>Enhancer</code> 409 * instead of this static method. 410 * @param type class to extend or interface to implement 411 * @param interfaces array of interfaces to implement, or null 412 * @param callback the callback to use for all methods 413 */ 414 public static Object create(Class superclass, Class interfaces[], Callback callback) { 415 Enhancer e = new Enhancer(); 416 e.setSuperclass(superclass); 417 e.setInterfaces(interfaces); 418 e.setCallback(callback); 419 return e.create(); 420 } 421 422 /*** 423 * Helper method to create an intercepted object. 424 * For finer control over the generated instance, use a new instance of <code>Enhancer</code> 425 * instead of this static method. 426 * @param type class to extend or interface to implement 427 * @param interfaces array of interfaces to implement, or null 428 * @param filter the callback filter to use when generating a new class 429 * @param callbacks callback implementations to use for the enhanced object 430 */ 431 public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) { 432 Enhancer e = new Enhancer(); 433 e.setSuperclass(superclass); 434 e.setInterfaces(interfaces); 435 e.setCallbackFilter(filter); 436 e.setCallbacks(callbacks); 437 return e.create(); 438 } 439 }

This page was automatically generated by Maven